๊ฐ๋ ฅํ ์ธ์ฆ์ผ๋ก Django REST Framework API๋ฅผ ๋ณดํธํ์ธ์. ์ค์ฉ์ ์ธ ์ฝ๋ ์์ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ํฌํจํ์ฌ ํ ํฐ ์ธ์ฆ๊ณผ JWT(JSON Web Token) ๊ตฌํ์ ๋น๊ตํฉ๋๋ค.
Python DRF ์ธ์ฆ: ๊ฒฌ๊ณ ํ API๋ฅผ ์ํ ํ ํฐ vs. JWT ๊ตฌํ
API ๋ณด์์ ๊ฐ์ฅ ์ค์ํฉ๋๋ค. Python ๋ฐ Django REST Framework(DRF)๋ก API๋ฅผ ๊ตฌ์ถํ ๋ ์ฌ์ฉํ ์ ์๋ ๋ช ๊ฐ์ง ์ธ์ฆ ์ต์ ์ด ์์ต๋๋ค. ์ด ๋ฌธ์์์๋ ๋ ๊ฐ์ง ์ธ๊ธฐ ์๋ ๋ฐฉ๋ฒ์ธ ํ ํฐ ์ธ์ฆ๊ณผ JWT(JSON Web Token) ์ธ์ฆ์ ์์ธํ ์ดํด๋ณด๊ณ , ๊ฐ์ ๊ณผ ์ฝ์ ์ ๋น๊ตํ๊ณ , ์ค์ ๊ตฌํ ์์ ๋ฅผ ์ ๊ณตํฉ๋๋ค.
API์์ ์ธ์ฆ ์ดํด
์ธ์ฆ์ API์ ์ก์ธ์คํ๋ ์ฌ์ฉ์ ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ID๋ฅผ ํ์ธํ๋ ํ๋ก์ธ์ค์ ๋๋ค. ์ ๊ตฌํ๋ ์ธ์ฆ ์์คํ ์ ๊ถํ์ด ์๋ ์ํฐํฐ๋ง ๋ณดํธ๋ ๋ฆฌ์์ค์ ์ก์ธ์คํ ์ ์๋๋ก ํฉ๋๋ค. RESTful API์ ์ปจํ ์คํธ์์ ์ธ์ฆ์ ์ผ๋ฐ์ ์ผ๋ก ๊ฐ ์์ฒญ๊ณผ ํจ๊ป ์๊ฒฉ ์ฆ๋ช (์: ์ฌ์ฉ์ ์ด๋ฆ ๋ฐ ๋น๋ฐ๋ฒํธ)์ ๋ณด๋ด๋ ๊ฒ์ ํฌํจํฉ๋๋ค. ๊ทธ๋ฌ๋ฉด ์๋ฒ๊ฐ ์ด๋ฌํ ์๊ฒฉ ์ฆ๋ช ์ ํ์ธํ๊ณ ์ ํจํ ๊ฒฝ์ฐ ์ก์ธ์ค ๊ถํ์ ๋ถ์ฌํฉ๋๋ค.
ํ ํฐ ์ธ์ฆ
ํ ํฐ ์ธ์ฆ์ ๊ฐ๋จํ๊ณ ์ง์ ์ ์ธ ๋ฉ์ปค๋์ฆ์ ๋๋ค. ์ฌ์ฉ์๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋ก๊ทธ์ธํ๋ฉด ์๋ฒ๋ ๊ณ ์ ํ ์์ ํ ํฐ์ ์์ฑํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ณ ์ฌ์ฉ์์๊ฒ ์ฐ๊ฒฐํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ํด๋ผ์ด์ธํธ๋ ํ์ ์์ฒญ์ 'Authorization' ํค๋์ ์ด ํ ํฐ์ ๋ณด๋ ๋๋ค. ์๋ฒ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํ ํฐ์ ๊ฒ์ํ๊ณ ์ ํจ์ฑ์ ํ์ธํ ํ ๊ทธ์ ๋ฐ๋ผ ์ก์ธ์ค ๊ถํ์ ๋ถ์ฌํฉ๋๋ค.
DRF๋ก ๊ตฌํ
DRF๋ ํ ํฐ ์ธ์ฆ์ ๋ํ ๊ธฐ๋ณธ ์ ๊ณต ์ง์์ ์ ๊ณตํฉ๋๋ค. ๊ตฌํ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- DRF๋ฅผ ์ค์นํ๊ณ Django ํ๋ก์ ํธ์ ๋ฑ๋กํฉ๋๋ค:
๋จผ์ Django REST Framework๊ฐ ์ค์น๋์ด ์๋์ง ํ์ธํ์ญ์์ค:
pip install djangorestframework
๊ทธ๋ฐ ๋ค์ `settings.py`์ `INSTALLED_APPS`์ ์ถ๊ฐํ์ญ์์ค:
INSTALLED_APPS = [
...
'rest_framework',
]
- ํ ํฐ ์ธ์ฆ ์ฒด๊ณ๋ฅผ ๊ธฐ๋ณธ ์ธ์ฆ ํด๋์ค๋ก ์ถ๊ฐํฉ๋๋ค(์ ํ ์ฌํญ์ด์ง๋ง ๊ถ์ฅ๋จ):
`settings.py` ํ์ผ์ ๋ค์์ ์ถ๊ฐํฉ๋๋ค:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}
์ด๋ ๊ฒ ํ๋ฉด API ์ ์ฒด์ ํ ํฐ ์ธ์ฆ์ด ์ ์ญ์ ์ผ๋ก ์ ์ฉ๋ฉ๋๋ค. `SessionAuthentication`์ ๋ธ๋ผ์ฐ์ ๊ธฐ๋ฐ ์ํธ ์์ฉ์ ์ํด ํฌํจ๋์ง๋ง ์์ ํ API ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ ์ ๊ฑฐํ ์ ์์ต๋๋ค.
- ๊ฐ ์ฌ์ฉ์์ ๋ํ ํ ํฐ์ ๋ง๋ญ๋๋ค:
์ ํธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์ถ๊ฐํ์ฌ ์ฌ์ฉ์ ์์ฑ ์ ์๋์ผ๋ก ํ ํฐ์ ๋ง๋ค ์ ์์ต๋๋ค. ์ฑ์์ `signals.py`๋ผ๋ ํ์ผ์ ๋ง๋ญ๋๋ค(์: `users/signals.py`):
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
๊ทธ๋ฐ ๋ค์ ์ฑ ๊ตฌ์ฑ ํด๋์ค์ `ready` ๋ฉ์๋ ๋ด์์ `users/apps.py` ํ์ผ์์ ์ด `signals.py` ํ์ผ์ ๊ฐ์ ธ์ต๋๋ค. `users/apps.py`์ ์:
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = 'django.db.BigAutoField'
name = 'users'
def ready(self):
import users.signals
์ด์ ๋ช ๋ น์ค์ ์ฌ์ฉํ์ฌ ํ ํฐ์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค:
python manage.py drf_create_token <username>
- API ๋ทฐ๋ฅผ ๊ตฌํํฉ๋๋ค:
ํ ํฐ ์ธ์ฆ์ด ํ์ํ ๋ทฐ์ ๊ฐ๋จํ ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [TokenAuthentication]
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
content = {
'message': 'Hello, ' + request.user.username + '! You are authenticated.',
}
return Response(content)
์ด ์์์ `authentication_classes`๋ ํ ํฐ ์ธ์ฆ์ ์ฌ์ฉํด์ผ ํจ์ ์ง์ ํ๊ณ `permission_classes`๋ ์ธ์ฆ๋ ์ฌ์ฉ์๋ง ๋ทฐ์ ์ก์ธ์คํ ์ ์์์ ์ง์ ํฉ๋๋ค.
- ๋ก๊ทธ์ธ API ๋ทฐ ํฌํจ:
์ฑ๊ณต์ ์ธ ๋ก๊ทธ์ธ ์ ํ ํฐ์ ์์ฑํ๋ ์๋ํฌ์ธํธ๋ ํ์ํฉ๋๋ค.
from django.contrib.auth import authenticate
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(username=username, password=password)
if user:
token, _ = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
else:
return Response({'error': 'Invalid Credentials'}, status=status.HTTP_401_UNAUTHORIZED)
ํ ํฐ ์ธ์ฆ์ ์ฅ์
- ๋จ์์ฑ: ๊ตฌํํ๊ณ ์ดํดํ๊ธฐ ์ฝ์ต๋๋ค.
- ์ํ ๋น์ ์ฅ: ๊ฐ ํ ํฐ ์์ฒญ์๋ ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ ์ ๋ณด๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
ํ ํฐ ์ธ์ฆ์ ๋จ์
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ข ์์ฑ: ํ ํฐ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ธฐ ์ํด ๊ฐ ์์ฒญ์ ๋ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ๊ฐ ํ์ํฉ๋๋ค. ์ด๋ ํนํ ๊ท๋ชจ์ ๋ฐ๋ผ ์ฑ๋ฅ์ ์ํฅ์ ์ค ์ ์์ต๋๋ค.
- ํ ํฐ ํด์ง: ํ ํฐ์ ํด์งํ๋ ค๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํ ํฐ์ ์ญ์ ํด์ผ ํ๋ฉฐ ์ด๋ ๋ณต์กํ ์ ์์ต๋๋ค.
- ํ์ฅ์ฑ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค๋ฒํค๋๋ก ์ธํด ๋๊ท๋ชจ ํธ๋ํฝ์ด ๋ง์ API์ ๊ฐ์ฅ ์ ํฉํ ์๋ฃจ์ ์ด ์๋ ์ ์์ต๋๋ค.
JWT(JSON Web Token) ์ธ์ฆ
JWT ์ธ์ฆ์ ๋์ฑ ํ๋์ ์ด๊ณ ์ ๊ตํ ์ ๊ทผ ๋ฐฉ์์ ๋๋ค. JWT๋ ์ฌ์ฉ์์ ๋ํ ํด๋ ์์ด ํฌํจ๋ ์๊ณ URL ์์ ํ JSON ๊ฐ์ฒด์ ๋๋ค. ์ด๋ฌํ ํด๋ ์์ ๋น๋ฐ ํค ๋๋ ๊ณต๊ฐ/๊ฐ์ธ ํค ์์ ์ฌ์ฉํ์ฌ ๋์งํธ ์๋ช ๋ฉ๋๋ค. ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธํ๋ฉด ์๋ฒ๋ JWT๋ฅผ ์์ฑํ์ฌ ํด๋ผ์ด์ธํธ์ ๋ณด๋ ๋๋ค. ๊ทธ๋ฐ ๋ค์ ํด๋ผ์ด์ธํธ๋ ํ์ ์์ฒญ์ 'Authorization' ํค๋์ ์ด JWT๋ฅผ ํฌํจํฉ๋๋ค. ์๋ฒ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ก์ธ์คํ ํ์ ์์ด JWT์ ์๋ช ์ ํ์ธํ ์ ์์ผ๋ฏ๋ก ๋ณด๋ค ํจ์จ์ ์ด๊ณ ํ์ฅ ๊ฐ๋ฅํ ์๋ฃจ์ ์ ๋๋ค.
DRF๋ก ๊ตฌํ
DRF๋ JWT ์ธ์ฆ์ ๋ํ ๊ธฐ๋ณธ ์ ๊ณต ์ง์์ ์ ๊ณตํ์ง ์์ง๋ง ๋ช ๊ฐ์ง ํ๋ฅญํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฝ๊ฒ ํตํฉํ ์ ์์ต๋๋ค. ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๊ฒ ์ค ํ๋๋ `djangorestframework-simplejwt`์ ๋๋ค.
- `djangorestframework-simplejwt` ์ค์น:
pip install djangorestframework-simplejwt
- DRF ์ค์ ๊ตฌ์ฑ:
`settings.py` ํ์ผ์ ๋ค์์ ์ถ๊ฐํฉ๋๋ค:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': settings.SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
}
์ค์ ์ค๋ช :
- `ACCESS_TOKEN_LIFETIME`: ์ก์ธ์ค ํ ํฐ์ด ์ ํจํ ๊ธฐ๊ฐ(์: 5๋ถ).
- `REFRESH_TOKEN_LIFETIME`: ์๋ก ๊ณ ์นจ ํ ํฐ์ด ์ ํจํ ๊ธฐ๊ฐ(์: 1์ผ). ์๋ก ๊ณ ์นจ ํ ํฐ์ ์ฌ์ฉ์๊ฐ ๋ค์ ๋ก๊ทธ์ธํ์ง ์๊ณ ๋ ์ ์ก์ธ์ค ํ ํฐ์ ์ป๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- `ROTATE_REFRESH_TOKENS`: ๊ฐ ์ฌ์ฉ ํ ์๋ก ๊ณ ์นจ ํ ํฐ์ ํ์ ํ ์ง ์ฌ๋ถ.
- `BLACKLIST_AFTER_ROTATION`: ํ์ ํ ์ด์ ์๋ก ๊ณ ์นจ ํ ํฐ์ ๋ธ๋๋ฆฌ์คํธ์ ์ถ๊ฐํ ์ง ์ฌ๋ถ.
- `ALGORITHM`: JWT ์๋ช ์ ์ฌ์ฉ๋๋ ์๊ณ ๋ฆฌ์ฆ(HS256์ ์ผ๋ฐ์ ์ธ ์ ํ์).
- `SIGNING_KEY`: JWT ์๋ช ์ ์ฌ์ฉ๋๋ ๋น๋ฐ ํค(์ผ๋ฐ์ ์ผ๋ก Django SECRET_KEY).
- `AUTH_HEADER_TYPES`: ์ธ์ฆ ํค๋ ์ ํ(์ผ๋ฐ์ ์ผ๋ก "Bearer").
- ๋ก๊ทธ์ธ ๋ฐ ์๋ก ๊ณ ์นจ ํ ํฐ API ๋ทฐ ํฌํจ:
`djangorestframework-simplejwt`๋ ํ ํฐ์ ์ป๊ณ ์๋ก ๊ณ ์น๋ ๋ทฐ๋ฅผ ์ ๊ณตํฉ๋๋ค. `urls.py`์ ํฌํจํฉ๋๋ค.
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
`TokenObtainPairView`๋ ์ฑ๊ณต์ ์ธ ์ธ์ฆ ํ ์ก์ธ์ค ๋ฐ ์๋ก ๊ณ ์นจ ํ ํฐ์ ์ ๊ณตํฉ๋๋ค. `TokenRefreshView`๋ ์ ํจํ ์๋ก ๊ณ ์นจ ํ ํฐ์ด ์ ๊ณต๋ ๋ ์ ์ก์ธ์ค ํ ํฐ์ ์ ๊ณตํฉ๋๋ค.
- API ๋ทฐ๋ฅผ ๊ตฌํํฉ๋๋ค:
JWT ์ธ์ฆ์ด ํ์ํ ๋ทฐ์ ๊ฐ๋จํ ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.authentication import JWTAuthentication
class ExampleView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
content = {
'message': 'Hello, ' + request.user.username + '! You are authenticated.',
}
return Response(content)
ํ ํฐ ์ธ์ฆ ์์ ๋ง์ฐฌ๊ฐ์ง๋ก `authentication_classes`๋ JWT ์ธ์ฆ์ ์ฌ์ฉํด์ผ ํจ์ ์ง์ ํ๊ณ `permission_classes`๋ ์ธ์ฆ๋ ์ฌ์ฉ์๋ง ์ก์ธ์ค๋ฅผ ์ ํํฉ๋๋ค.
JWT ์ธ์ฆ์ ์ฅ์
- ํ์ฅ์ฑ: ํ ํฐ ์ ํจ์ฑ ๊ฒ์ฌ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ๊ฐ ํ์ํ์ง ์์ผ๋ฏ๋ก ํ์ฅ์ฑ์ด ๋ ๋ฐ์ด๋ฉ๋๋ค.
- ์ํ ๋น์ ์ฅ: JWT์๋ ์ธ์ฆ์ ํ์ํ ๋ชจ๋ ์ ๋ณด๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
- ํ์คํ: JWT๋ ๋๋ฆฌ ์ฑํ๋ ํ์ค์ด๋ฉฐ ๋ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ ํ๋ซํผ์์ ์ง์๋ฉ๋๋ค.
- ๋ง์ดํฌ๋ก ์๋น์ค ์นํ์ : ์๋น์ค๊ฐ JWT๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ํ์ธํ ์ ์์ผ๋ฏ๋ก ๋ง์ดํฌ๋ก ์๋น์ค ์ํคํ ์ฒ์ ์ ํฉํฉ๋๋ค.
JWT ์ธ์ฆ์ ๋จ์
- ๋ณต์ก์ฑ: ํ ํฐ ์ธ์ฆ๋ณด๋ค ๊ตฌํ์ด ๋ ๋ณต์กํฉ๋๋ค.
- ํ ํฐ ํฌ๊ธฐ: JWT๋ ๋จ์ ํ ํฐ๋ณด๋ค ํด ์ ์์ผ๋ฏ๋ก ๋์ญํญ ์ฌ์ฉ๋์ด ์ฆ๊ฐํ ์ ์์ต๋๋ค.
- ํ ํฐ ํด์ง: JWT๋ฅผ ํด์งํ๋ ๊ฒ์ ์ด๋ ต์ต๋๋ค. ์ผ๋จ ๋ฐ๊ธ๋๋ฉด ๋ง๋ฃ๋ ๋๊น์ง ์ ํจํฉ๋๋ค. ํด๊ฒฐ ๋ฐฉ๋ฒ์๋ ํด์ง๋ ํ ํฐ์ ๋ธ๋๋ฆฌ์คํธ์ ์ถ๊ฐํ๋ ๊ฒ์ด ํฌํจ๋๋ฉฐ, ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ข ์์ฑ์ ๋ค์ ๋์ ํฉ๋๋ค.
ํ ํฐ ํด์ง ์ ๋ต
ํ ํฐ ๋ฐ JWT ์ธ์ฆ ๋ฐฉ๋ฒ ๋ชจ๋ ์ก์ธ์ค ๊ถํ์ ํด์งํ๋ ๋ฉ์ปค๋์ฆ์ด ํ์ํฉ๋๋ค. ํ ํฐ ํด์ง์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
ํ ํฐ ์ธ์ฆ ํด์ง
ํ ํฐ ์ธ์ฆ์ ์ฌ์ฉํ๋ฉด ํด์ง๊ฐ ๊ฐ๋จํฉ๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํ ํฐ์ ์ญ์ ํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
from rest_framework.authtoken.models import Token
try:
token = Token.objects.get(user=request.user)
token.delete()
except Token.DoesNotExist:
pass
JWT ์ธ์ฆ ํด์ง
JWT ํด์ง๋ ํ ํฐ ์์ฒด๊ฐ ์์ฒด ํฌํจ๋์ด ์๊ณ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ๋ฅผ (์ฒ์์) ์ฌ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ ๋ณต์กํฉ๋๋ค. ์ผ๋ฐ์ ์ธ ์ ๋ต์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ํ ํฐ ๋ธ๋๋ฆฌ์คํธ: ํด์ง๋ ํ ํฐ์ ๋ธ๋๋ฆฌ์คํธ(์: ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ ๋๋ Redis ์บ์)์ ์ ์ฅํฉ๋๋ค. JWT ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ธฐ ์ ์ ๋ธ๋๋ฆฌ์คํธ์ ์๋์ง ํ์ธํฉ๋๋ค. `djangorestframework-simplejwt`๋ ์๋ก ๊ณ ์นจ ํ ํฐ ๋ธ๋๋ฆฌ์คํธ์ ๋ํ ๊ธฐ๋ณธ ์ ๊ณต ์ง์์ ์ ๊ณตํฉ๋๋ค.
- ์งง์ ๋ง๋ฃ ์๊ฐ: ์งง์ ์ก์ธ์ค ํ ํฐ ๋ง๋ฃ ์๊ฐ์ ์ฌ์ฉํ๊ณ ์๋ก ๊ณ ์นจ ํ ํฐ์ ์์กดํ์ฌ ์ ์ก์ธ์ค ํ ํฐ์ ์์ฃผ ์ป์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์์๋ ํ ํฐ์ ์ฌ์ฉํ ์ ์๋ ๊ธฐํ ์ฐฝ์ด ์ ํ๋ฉ๋๋ค.
- ์๋ก ๊ณ ์นจ ํ ํฐ ํ์ : ๊ฐ ์ฌ์ฉ ํ ์๋ก ๊ณ ์นจ ํ ํฐ์ ํ์ ํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋งค๋ฒ ์ด์ ํ ํฐ์ด ๋ฌดํจํ๋๊ณ ํ ํฐ ๋๋์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
OAuth2 ๋ฐ OpenID Connect
๋ ๋ณต์กํ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ ์๋๋ฆฌ์ค์ ๊ฒฝ์ฐ OAuth2 ๋ฐ OpenID Connect๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ด๋ฌํ ํ์ค์ ์๊ฒฉ ์ฆ๋ช ์ ๊ณต์ ํ์ง ์๊ณ ๋ฆฌ์์ค์ ๋ํ ์ก์ธ์ค ๊ถํ์ ์์ํ๊ธฐ ์ํ ๊ฐ๋ ฅํ ํ๋ ์์ํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. OAuth2๋ ์ฃผ๋ก ๊ถํ ๋ถ์ฌ ํ๋กํ ์ฝ์ด๊ณ OpenID Connect๋ OAuth2๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ถ๋์ด ์ธ์ฆ ์๋น์ค๋ฅผ ์ ๊ณตํฉ๋๋ค. `django-oauth-toolkit` ๋ฐ `django-allauth`์ ๊ฐ์ ์ฌ๋ฌ Django ํจํค์ง๋ OAuth2 ๋ฐ OpenID Connect๋ฅผ DRF API์ ํตํฉํ๋ ๊ฒ์ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
์์ ์๋๋ฆฌ์ค: ์ฌ์ฉ์๊ฐ ํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ API์ ์ ์ฅ๋ ๋ฐ์ดํฐ์ ๋ํ ์ก์ธ์ค ๊ถํ์ ๋ถ์ฌํ๋ ค๊ณ ํฉ๋๋ค. OAuth2๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ์ฉ์๋ ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ๊ณต์ ํ์ง ์๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ์น์ธํ ์ ์์ต๋๋ค. ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์๋ ๊ถํ ๋ฒ์ ๋ด์์ ์ฌ์ฉ์ ๋ฐ์ดํฐ์ ์ก์ธ์คํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ์ก์ธ์ค ํ ํฐ์ ๋ฐ์ต๋๋ค.
์ฌ๋ฐ๋ฅธ ์ธ์ฆ ๋ฐฉ๋ฒ ์ ํ
๊ฐ์ฅ ์ ํฉํ ์ธ์ฆ ๋ฐฉ๋ฒ์ ํน์ ์๊ตฌ ์ฌํญ์ ๋ฐ๋ผ ๋ค๋ฆ ๋๋ค.
- ๋จ์์ฑ ๋ฐ ๊ตฌํ ์๋: ํ ํฐ ์ธ์ฆ์ ์ผ๋ฐ์ ์ผ๋ก ์ด๊ธฐ์ ๊ตฌํํ๊ธฐ๊ฐ ๋ ์ฝ์ต๋๋ค.
- ํ์ฅ์ฑ: JWT ์ธ์ฆ์ ํธ๋ํฝ์ด ๋ง์ API์ ๋ ํ์ฅ ๊ฐ๋ฅํฉ๋๋ค.
- ๋ณด์ ์๊ตฌ ์ฌํญ: ๋ฐ์ดํฐ์ ๋ฏผ๊ฐ๋์ ํ์ํ ๋ณด์ ์์ค์ ๊ณ ๋ คํ์ญ์์ค. OAuth2/OpenID Connect๋ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๋ณด์ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ง ๊ตฌํ์ด ๋ ๋ณต์กํฉ๋๋ค.
- ๋ง์ดํฌ๋ก ์๋น์ค ์ํคํ ์ฒ: ๊ฐ ์๋น์ค๊ฐ ํ ํฐ์ ๋ ๋ฆฝ์ ์ผ๋ก ํ์ธํ ์ ์์ผ๋ฏ๋ก JWT๋ ๋ง์ดํฌ๋ก ์๋น์ค์ ์ ํฉํฉ๋๋ค.
API ์ธ์ฆ์ ์ํ ๋ชจ๋ฒ ์ฌ๋ก
- HTTPS ์ฌ์ฉ: ํญ์ HTTPS๋ฅผ ์ฌ์ฉํ์ฌ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ํต์ ์ ์ํธํํ๊ณ ์๊ฒฉ ์ฆ๋ช ์ด ๋์ฒญ๋์ง ์๋๋ก ๋ณดํธํฉ๋๋ค.
- ๋น๋ฐ์ ์์ ํ๊ฒ ์ ์ฅ: ๋น๋ฐ ํค ๋๋ ๋น๋ฐ๋ฒํธ๋ฅผ ์ผ๋ฐ ํ ์คํธ๋ก ์ ์ฅํ์ง ๋ง์ญ์์ค. ํ๊ฒฝ ๋ณ์ ๋๋ ์์ ํ ๊ตฌ์ฑ ๊ด๋ฆฌ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ญ์์ค.
- ์๋ ์ ํ ๊ตฌํ: ์๋ ์ ํ์ ๊ตฌํํ์ฌ ํด๋ผ์ด์ธํธ๊ฐ ์ฃผ์ด์ง ๊ธฐ๊ฐ ๋ด์ ์ํํ ์ ์๋ ์์ฒญ ์๋ฅผ ์ ํํ์ฌ API๋ฅผ ๋จ์ฉ์ผ๋ก๋ถํฐ ๋ณดํธํฉ๋๋ค.
- ์ ๋ ฅ ์ ํจ์ฑ ๊ฒ์ฌ: ๋ชจ๋ ์ ๋ ฅ ๋ฐ์ดํฐ์ ์ ํจ์ฑ์ ์ฒ ์ ํ ๊ฒ์ฌํ์ฌ ์ฃผ์ ๊ณต๊ฒฉ์ ๋ฐฉ์งํฉ๋๋ค.
- ๋ชจ๋ํฐ๋ง ๋ฐ ๋ก๊น : API์์ ์์ฌ์ค๋ฌ์ด ํ๋์ ๋ชจ๋ํฐ๋งํ๊ณ ๊ฐ์ฌ ๋ชฉ์ ์ผ๋ก ์ธ์ฆ ์ด๋ฒคํธ๋ฅผ ๊ธฐ๋กํฉ๋๋ค.
- ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ๊ธฐ์ ์ผ๋ก ์ ๋ฐ์ดํธ: ๋ณด์ ํจ์น ๋ฐ ๊ฐ์ ์ฌํญ์ ์ด์ ์ ์ป์ผ๋ ค๋ฉด Django, DRF ๋ฐ ์ธ์ฆ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ต์ ์ํ๋ก ์ ์งํ์ญ์์ค.
- CORS(๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ ) ๊ตฌํ: ์ ๋ขฐํ ์ ์๋ ๋๋ฉ์ธ๋ง ์น ๋ธ๋ผ์ฐ์ ์์ API์ ์ก์ธ์คํ ์ ์๋๋ก CORS๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌ์ฑํฉ๋๋ค.
๊ฒฐ๋ก
์ ์ ํ ์ธ์ฆ ๋ฐฉ๋ฒ์ ์ ํํ๋ ๊ฒ์ DRF API๋ฅผ ๋ณดํธํ๋ ๋ฐ ๋งค์ฐ ์ค์ํฉ๋๋ค. ํ ํฐ ์ธ์ฆ์ ๋จ์์ฑ์ ์ ๊ณตํ๊ณ JWT ์ธ์ฆ์ ํ์ฅ์ฑ๊ณผ ์ ์ฐ์ฑ์ ์ ๊ณตํฉ๋๋ค. API ๋ณด์์ ๋ํ ๋ชจ๋ฒ ์ฌ๋ก์ ํจ๊ป ๊ฐ ๋ฐฉ๋ฒ์ ์ฅ๋จ์ ์ ์ดํดํ๋ฉด ๋ฐ์ดํฐ์ ์ฌ์ฉ์๋ฅผ ๋ณดํธํ๋ ๊ฐ๋ ฅํ๊ณ ์์ ํ API๋ฅผ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
ํน์ ์๊ตฌ ์ฌํญ์ ๊ณ ๋ คํ๊ณ ๋ณด์, ์ฑ๋ฅ ๋ฐ ๊ตฌํ ์ฉ์ด์ฑ์ ๊ท ํ์ ๊ฐ์ฅ ์ ๋ง์ถ๋ ์๋ฃจ์ ์ ์ ํํ๋ ๊ฒ์ ์์ง ๋ง์ญ์์ค. ๋ ๋ณต์กํ ๊ถํ ๋ถ์ฌ ์๋๋ฆฌ์ค์ ๊ฒฝ์ฐ OAuth2 ๋ฐ OpenID Connect๋ฅผ ์ดํด๋ณด์ญ์์ค.